const path = require('path')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { DefinePlugin, optimize } = require('webpack')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const VueLoaderPlugin = require('vue-loader/lib/plugin')
const TerserPlugin = require("terser-webpack-plugin") // 压缩代码 webpack5以上自带的 
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin') // react热替换 应为组件没有进行热替换
const isDevelopment = process.env.NODE_ENV !== 'production' // true是开发环境 false为生产环境
const MiniCssExtractPlugin = require('mini-css-extract-plugin') // 抽离css成独立的文件
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin') // css压缩
const PurgeCssPlugin = require('purgecss-webpack-plugin') // 删除未使用的css
const glob = require('glob') // 默认安装
const CompressionPlugin = require('compression-webpack-plugin') // gzip压缩
const InlineChunkHtmlPlugin = require('react-dev-utils/InlineChunkHtmlPlugin') // 将一些代码内联到html中
const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer") // 打包大小分析的插件
const AutoUploadPlugin = require("./src/js/myplugin") // 自定义插件plugin
module.exports = {
  // target: "web", // 这个和devServer中的hot一起开不设置target会有问题（未发现）
  mode: isDevelopment ? 'development' : 'production',
  entry: "./src/main.js",
  devtool: isDevelopment ? 'source-map' : false,
  output: {
    filename: "js/[name].[chunkhash:6]./bundle.js",
    // chunkFilename 指未被列在 entry 中，却又需要被打包出来的 chunk 文件的名称。一般来说，这个 chunk 文件指的就是要懒加载的代码。
    chunkFilename: "js/[name].[contenthash:6].chunk.js",
    // 必须是一个绝对路径
    path: path.resolve(__dirname, "./build"),
    // assetModuleFilename: "img/[name].[hash:6][ext]"
    // 在打包之后的静态资源前面进行一个路径的拼接 本地运行用./
    // publicPath: "/",
  },
  optimization: {
    minimize: true,
    // 对代码进行压缩相关的操作丑化等 
    minimizer: [
      new TerserPlugin({
        extractComments: false,  // 抽取对应注释成为单独的文件
      }),
    ],
    /* natural: 使用自然数(不推荐),
    named: 使用包所在目录作为name(在开发环境推荐)
    deterministic: 生成id, 针对相同文件生成的id是不变 */
    /* chunkIds: "deterministic", */
    splitChunks: {
      // async异步导入
      // initial同步导入
      // all 异步/同步导入
      chunks: "all",
      // 最小尺寸: 如果拆分出来一个, 那么拆分出来的这个包的大小最小为minSize
      minSize: 20000,
      // 将大于maxSize的包, 拆分成不小于minSize的包
      maxSize: 20000,
      // minChunks表示引入的包, 至少被导入了几次
      minChunks: 1,
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          filename: "js/[id]_vendors.js",
          // name: "vendor-chunks.js",
          priority: -10
        },
        default: {
          minChunks: 2,
          filename: "js/common_[id].js",
          priority: -20
        }
      }
    },
    // true/multiple
    // single
    // object: name
    /* runtimeChunk: {
      name: function (entrypoint) {
        return `ss${entrypoint.name}`
      }
    } */
  },
  // 配置cdn的然后在public中的html模板中引入cdn链接
  externals: isDevelopment ? {} : {
    // window._
    lodash: "_",
  },
  module: {
    rules: [
      {
        test: /\.jsx?$/i, // i 无论大小写
        exclude: /node_modules/, // 改文件下面的jsx文件不需要转化es5
        // use: [ "babel-loader", "eslint-loader" ],
        use: {
          loader: "babel-loader",
          /* options: {
             presets: [
               ["@babel/preset-env", {
                 targets: ["chrome 88"],
                 enmodules: true
               }]
             ],
             plugins: [
              "@babel/plugin-transform-arrow-functions",
              "@babel/plugin-transform-block-scoping"
            ]
          } */
        }
      },
      // 处理js 中的es语法 可以去掉和上面的jsx配置是一样的
      {
        test: /\.js$/,
        exclude: /node_modules/, // 这个文件不适用babel 否则引发冲突
        use: {
          loader: "babel-loader",
          /* options: {
            presets: ["@babel/preset-env"]
          } */
        }
      },
      // .需要转义用\ $指的是以谁为结尾的文件
      {
        // 规则使用正则表达式
        test: /\.css$/i, // 匹配资源
        use: [
          // { loader: "css-loader" },
          // 注意: 编写顺序(从下往上, 从右往做, 从后往前)
          isDevelopment ? "style-loader" : MiniCssExtractPlugin.loader, // style-loader把css插入到页面中
          {
            loader: "css-loader", // 可以识别css文件
            options: {
              importLoaders: 1
            }
          },
          "postcss-loader",
        ],
        // 这个作用是sideEffects  当没有副作用的时候会直接删除这个包
        // "sideEffects": true // react 脚手架中
        // loader: "css-loader" // 这个是简写方式
      },
      {
        test: /\.less$/,
        use: [
          "style-loader",
          {
            loader: "css-loader",
            options: {
              importLoaders: 2 // 每次加载都会重新应用 postcss-loader less-loader 
            }
          },
          "postcss-loader",
          "less-loader"
        ]
      },
      /* {
        test: /\.(png|jpe?g|gif|svg)$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              name: "img/[name].[hash:6].[ext]",
              limit: 100 * 1024
            }
          }
        ]
      } */
      {
        test: /\.(png|jpe?g|gif|svg)$/,
        // type: "asset/resource", file-loader的效果
        // type: "asset/inline", url-loader的效果
        type: "asset",
        generator: {
          filename: "img/[name].[hash:6][ext]",
        },
        parser: {
          dataUrlCondition: {
            maxSize: 1000 * 1024,
          }
        }
      },
      // 用于处理字体文件的
      {
        test: /\.ttf|eot|woff2?$/i,
        type: "asset/resource",
        generator: {
          filename: "font/[name].[hash:6][ext]",
        }
      },
      {
        test: /\.ts$/,
        exclude: /node_modules/,
        // 本质上是依赖于typescript(typescript compiler)
        use: "babel-loader"
      },
      {
        test: /\.vue$/i,
        use: "vue-loader"
      },
      // 自定义loader
      /* {
         test: /\.js$/i,
         use: {
           // loader: "./src/js/myloader.js",
           loader: "myloader",
           options: {
             name: "why",
             age: 18
           }
         }
      }, */
    ]
  },
  plugins: [
    isDevelopment ? new ReactRefreshWebpackPlugin() : () => {}, // 对react的组件进行热替换 开发环境加载否则build报错 且生产环境要返回个function才不会报错
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      title: "这是个标题有",
      template: "./public/index.html",
      // inject: "body",
      cache: true, // 当文件没有发生任何改变时, 直接使用之前的缓存
      /* minify: !isDevelopment ? { // 生产模式下压缩
        removeComments: false, // 是否要移除注释
        removeRedundantAttributes: false, // 是否移除多余的属性
        removeEmptyAttributes: true, // 是否移除一些空属性
        collapseWhitespace: false,
        removeStyleLinkTypeAttributes: true,
        minifyCSS: true,
        minifyJS: {
          mangle: {
            toplevel: true
          }
        }
      } : false */
    }),
    new DefinePlugin({
      BASE_URL: '"./"',
    }),
    // 把public文件夹复制到build中但是要省略index.html abc.txt .DS_Store
    new CopyWebpackPlugin({
      patterns: [
        {
          from: "public",
          globOptions: {
            ignore: [
              "**/index.html",
              "**/.DS_Store",
              "**/abc.txt"
            ]
          }
        }
      ]
    }),
    new VueLoaderPlugin(), // 解析vue时候必须使用这个插件
    new MiniCssExtractPlugin({
      filename: "css/[name].[contenthash:6].css"
    }),
    !isDevelopment ? new optimize.ModuleConcatenationPlugin() : () => {}, // 生产环境下使用优化打包后的作用域提升
    !isDevelopment ? new CssMinimizerPlugin() : () => {}, // 生产环境下使用css压缩
    !isDevelopment ? new PurgeCssPlugin({
      paths: glob.sync(`${path.resolve(__dirname, "./src")}/**/*`, { nodir: true }), // nodir 不是文件夹
      safelist: function () {
        return {
          standard: ["body", "html"] // 忽略body， 和html
        }
      }
    }) : () => {}, // 删除未使用的css 生产环境下使用
    new CompressionPlugin({
      test: /\.(css|js)$/i, // 压缩css 和 js
      threshold: 0,
      minRatio: 0.8,
      algorithm: "gzip", // 压缩算法
      // exclude
      // include
    }),
    new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [/runtime.*\.js/,]), // 主要作用性能优化
    // !isDevelopment ? new BundleAnalyzerPlugin() : () => {}, // 打包分析
    new AutoUploadPlugin({ // 自定义plugin
      host: "123.207.32.32",
      username: "root",
      password: "",
      removePath: "/root/test"
    }),
  ],
  /* resolveLoader: {
    modules: ["node_modules", "./src/js"] // 自定义loader是需要些路径 用这个方法只需要写myloader.js
  }, */
  // 专门为webpack-dev-server
  devServer: {
    hot: true,
    hotOnly: true,
    // host: "0.0.0.0", // 让其他的电脑可以访问这个地址
    // port: 8080,
    // 本地服务所在的文件夹
    publicPath: "/",
    proxy: {
      "/why": {
        target: "http://localhost:8888",
        pathRewrite: {
          "^/why": ""
        },
        secure: false,
        changeOrigin: true
      },
      historyApiFallback: true
    },
  },
  resolve: {
    extensions: ['.wasm', '.mjs', '.js', '.json', '.jsx', '.ts', '.vue'], // 可以省略后缀
    alias: {
      "@": path.resolve(__dirname, "./src"),
      "img": path.resolve(__dirname, "./src/img")
    }
  },
}